import {
  cloneDeep,
  intersection,
  isAllEmpty,
  isIncludeAllChildren,
  isString,
  storageLocal,
} from '@pureadmin/utils'
import { useTimeoutFn } from '@vueuse/core'
import { isProxy, toRaw } from 'vue'
import {
  createWebHashHistory,
  createWebHistory,
  type RouteComponent,
  type RouteRecordRaw,
  type RouterHistory,
} from 'vue-router'
import { userApi } from '@/api'
import { type menuType, routerArrays } from '@/layout/types'
import { transformRouters } from '@/router/router-transform'
import { useMultiTagsStore } from '@/store/modules/multiTags'
import { usePermissionStore } from '@/store/modules/permission'
import { type DataInfo, userKey } from '@/utils/auth'
import { buildHierarchyTree } from '@/utils/tree'
import { router } from './index'

// 动态路由

const IFrame = () => import('@/layout/frame.vue')
// https://cn.vitejs.dev/guide/features.html#glob-import
const modulesRoutes = import.meta.glob('/src/views/**/*.{vue,tsx}')

/**
 * 查找匹配的组件路径，优先查找 index.vue 文件
 *
 * @param componentPath 组件路径
 * @param modulesRoutesKeys 所有模块路由的键
 * @returns 匹配的索引，-1 表示未找到
 */
function findComponentIndex(componentPath: any, modulesRoutesKeys: string[]): number {
  if (!componentPath) return -1

  const pathStr = String(componentPath)

  // 优先查找 index.vue 或 index.tsx 文件
  const indexPatterns = ['/index.vue', '/index.tsx']
  for (const pattern of indexPatterns) {
    const indexIndex = modulesRoutesKeys.findIndex(
      key => key.includes(pathStr) && key.endsWith(pattern),
    )
    if (indexIndex !== -1) {
      return indexIndex
    }
  }

  // 如果没找到 index 文件，再查找其他匹配的文件
  return modulesRoutesKeys.findIndex(key => key.includes(pathStr))
}

function handRank(routeInfo: any) {
  const { name, path, parentId, meta } = routeInfo
  return isAllEmpty(parentId)
    ? isAllEmpty(meta?.rank) || (meta?.rank === 0 && name !== 'Home' && path !== '/')
    : false
}

/**
 * 按照路由中meta下的rank等级升序来排序路由
 *
 * @param arr
 */
function ascending(arr: any[]) {
  arr.forEach((v, index) => {
    // 当rank不存在时，根据顺序自动创建，首页路由永远在第一位
    if (handRank(v) && v?.meta?.rank) v.meta.rank = index + 2
  })
  return arr.sort((a: { meta: { rank: number } }, b: { meta: { rank: number } }) => {
    return (a?.meta?.rank || 0) - (b?.meta?.rank || 0)
  })
}

/**
 * 过滤meta中showLink为false的菜单
 *
 * @param data
 * @param isFilterShowLink
 */
function filterTree(data: RouteComponent[], isFilterShowLink = true) {
  const newTree = cloneDeep(data).filter((v: { meta: { showLink: boolean } }) =>
    isFilterShowLink ? v.meta?.showLink !== false : true,
  )
  newTree.forEach(
    (v: { children }) => v.children && (v.children = filterTree(v.children, isFilterShowLink)),
  )
  return newTree
}

/** 过滤children长度为0的的目录，当目录下没有菜单时，会过滤此目录，目录没有赋予roles权限，当目录下只要有一个菜单有显示权限，那么此目录就会显示 */
// function filterChildrenTree(data: RouteComponent[]) {
//   // 先处理子节点，避免先过滤父节点导致可见的子节点被连带过滤掉
//   const newTree = cloneDeep(data)
//   newTree.forEach((v: { children }) => v.children && (v.children = filterTree(v.children)))
//   // 然后再过滤没有子节点的目录
//   return newTree.filter((v: any) => v?.children?.length !== 0)
// }

/**
 * 判断两个数组彼此是否存在相同值
 *
 * @param a
 * @param b
 */
function isOneOfArray(a: Array<string>, b: Array<string>) {
  return Array.isArray(a) && Array.isArray(b) ? intersection(a, b).length > 0 : true
}

/**
 * 从localStorage里取出当前登录用户的角色roles，过滤无权限的菜单
 *
 * @param data
 */
function filterNoPermissionTree(data: RouteComponent[]) {
  const currentRoles = storageLocal().getItem<DataInfo<number>>(userKey)?.roles ?? []
  const newTree = cloneDeep(data).filter((v: any) => isOneOfArray(v.meta?.roles, currentRoles))
  newTree.forEach((v: any) => v.children && (v.children = filterNoPermissionTree(v.children)))
  // TODO: 这里需要过滤掉没有子节点的目录
  // return filterChildrenTree(newTree)
  return newTree
}

/**
 * 通过指定 `key` 获取父级路径集合，默认 `key` 为 `path`
 *
 * @param value
 * @param routes
 * @param key
 */
function getParentPaths(value: string, routes: RouteRecordRaw[], key = 'path') {
  // 深度遍历查找
  function dfs(routes: RouteRecordRaw[], value: string, parents: string[]) {
    for (const item of routes) {
      // 返回父级path
      if (item[key] === value) return parents
      // children不存在或为空则不递归
      if (!item.children || !item.children.length) continue
      // 往下查找时将当前path入栈
      parents.push(item.path)

      if (dfs(item.children, value, parents).length) return parents
      // 深度遍历查找未找到时当前path 出栈
      parents.pop()
    }
    // 未找到时返回空数组
    return []
  }

  return dfs(routes, value, [])
}

/**
 * 查找对应 `path` 的路由信息
 *
 * @param path
 * @param routes
 */
function findRouteByPath(path: string, routes: RouteRecordRaw[]) {
  let res = routes.find((item: { path: string }) => item.path == path)
  if (res) {
    return isProxy(res) ? toRaw(res) : res
  } else {
    for (const route of routes) {
      const children = route?.children ?? []
      if (children.length > 0) {
        res = findRouteByPath(path, children)
        if (res) {
          return isProxy(res) ? toRaw(res) : res
        }
      }
    }
    return null
  }
}

function addPathMatch() {
  if (!router.hasRoute('pathMatch')) {
    router.addRoute({ path: '/:pathMatch(.*)', name: 'pathMatch', redirect: '/error/404' })
  }
}

/**
 * 处理动态路由（后端返回的路由）
 *
 * @param routeList
 */
function handleAsyncRoutes(routeList) {
  const permissionStore = usePermissionStore()
  if (routeList.length === 0) {
    permissionStore.handleWholeMenus(routeList)
  } else {
    const asyncRoutes = addAsyncRoutes(routeList)
    if (asyncRoutes) {
      formatFlatteningRoutes(asyncRoutes)?.forEach((v: RouteRecordRaw) => {
        // 防止重复添加路由
        if (router.options.routes[0]?.children?.findIndex(value => value.path === v.path) !== -1) {
          return
        } else {
          // 切记将路由push到routes后还需要使用addRoute，这样路由才能正常跳转
          router.options.routes[0]?.children?.push(v)
          // 最终路由进行升序
          ascending(router.options.routes[0]?.children ?? [])
          if (!router.hasRoute(v?.name as never)) router.addRoute(v)
          const flattenRouters: any = router.getRoutes().find(n => n.path === '/')
          router.addRoute(flattenRouters)
        }
      })
      permissionStore.handleWholeMenus(routeList)
    }
  }

  const multiTagsStore = useMultiTagsStore()
  if (!multiTagsStore.getMultiTagsCache) {
    multiTagsStore.handleTags('equal', [
      ...routerArrays,
      ...permissionStore.flatteningRoutes.filter(v => v?.meta?.fixedTag),
    ])
  }
  addPathMatch()
}

/** 初始化路由（`new Promise` 写法防止在异步请求中造成无限循环）*/
function initRouter() {
  return new Promise(resolve => {
    userApi.getUserMenu().then(({ model }) => {
      const asyncRoutes = transformRouters(model)
      const datas = [...asyncRoutes]
      handleAsyncRoutes(cloneDeep(datas))
      resolve(router)
    })
  })
}

/**
 * 将多级嵌套路由处理成一维数组
 *
 * @param routesList 传入路由
 * @returns 返回处理后的一维路由
 */
function formatFlatteningRoutes(routesList: RouteRecordRaw[]) {
  if (routesList.length === 0) return routesList
  let hierarchyList = buildHierarchyTree(routesList)
  for (let i = 0; i < hierarchyList.length; i++) {
    if (hierarchyList[i].children) {
      hierarchyList = hierarchyList
        .slice(0, i + 1)
        .concat(hierarchyList[i].children, hierarchyList.slice(i + 1))
    }
  }
  return hierarchyList
}

/**
 * 一维数组处理成多级嵌套数组（三级及以上的路由全部拍成二级，keep-alive 只支持到二级缓存）
 * https://github.com/pure-admin/vue-pure-admin/issues/67
 *
 * @param routesList 处理后的一维路由菜单数组
 * @returns 返回将一维数组重新处理成规定路由的格式
 */
function formatTwoStageRoutes(routesList: RouteRecordRaw[]) {
  if (routesList.length === 0) return routesList
  const newRoutesList: RouteRecordRaw[] = []
  routesList.forEach((v: RouteRecordRaw) => {
    if (v.path === '/') {
      newRoutesList.push({
        component: v.component,
        name: v.name,
        path: v.path,
        redirect: v.redirect,
        meta: v.meta,
        children: [],
      })
    } else {
      newRoutesList[0]?.children?.push({ ...v })
    }
  })
  return newRoutesList
}

/**
 * 处理缓存路由（添加、删除、刷新）
 *
 * @param root0
 * @param root0.name
 * @param mode
 */
function handleAliveRoute({ name }: ToRouteType, mode?: string) {
  const permissionStore = usePermissionStore()
  switch (mode) {
    case 'add':
      permissionStore.cacheOperate({ mode: 'add', name })
      break
    case 'delete':
      permissionStore.cacheOperate({ mode: 'delete', name })
      break
    case 'refresh':
      permissionStore.cacheOperate({ mode: 'refresh', name })
      break
    default:
      permissionStore.cacheOperate({ mode: 'delete', name })
      useTimeoutFn(() => {
        permissionStore.cacheOperate({ mode: 'add', name })
      }, 100)
  }
}

/**
 * 过滤后端传来的动态路由 重新生成规范路由
 *
 * @param arrRoutes
 */
function addAsyncRoutes(arrRoutes: Array<menuType & RouteRecordRaw>) {
  if (!arrRoutes || !arrRoutes.length) return
  const modulesRoutesKeys = Object.keys(modulesRoutes)
  arrRoutes.forEach((v: menuType & RouteRecordRaw) => {
    // 将backstage属性加入meta，标识此路由为后端返回路由
    v.meta && (v.meta.backstage = true)
    // 父级的redirect属性取值：如果子级存在且父级的redirect属性不存在，默认取第一个子级的path；如果子级存在且父级的redirect属性存在，取存在的redirect属性，会覆盖默认值
    const child = v?.children && v.children.length ? v.children[0] : null
    if (child && !v.redirect && child.meta?.showLink) {
      v.redirect = child.path
    }

    // 父级的name属性取值：如果子级存在且父级的name属性不存在，默认取第一个子级的name；如果子级存在且父级的name属性存在，取存在的name属性，会覆盖默认值（注意：测试中发现父级的name不能和子级name重复，如果重复会造成重定向无效（跳转404），所以这里给父级的name起名的时候后面会自动加上`Parent`，避免重复）
    if (v?.children && v.children.length && !v.name)
      v.name = `${v.children[0].name as string}Parent`
    if (v.meta?.frameSrc) {
      v.component = IFrame
    } else {
      // 对后端传component组件路径和不传做兼容（如果后端传component组件路径，那么path可以随便写，如果不传，component组件路径会跟path保持一致）
      const index = v?.component
        ? findComponentIndex(v.component as any, modulesRoutesKeys)
        : v.url
          ? findComponentIndex(v.url as string, modulesRoutesKeys)
          : -1
      v.component = modulesRoutes[modulesRoutesKeys[index]]
    }

    if (v?.children && v.children.length) {
      addAsyncRoutes(v.children as any)
    }
  })
  return arrRoutes
}

/**
 * 获取路由历史模式 https://next.router.vuejs.org/zh/guide/essentials/history-mode.html
 *
 * @param routerHistory
 */
function getHistoryMode(routerHistory: string): RouterHistory {
  // len为1 代表只有历史模式 为2 代表历史模式中存在base参数 https://next.router.vuejs.org/zh/api/#%E5%8F%82%E6%95%B0-1
  const historyMode = routerHistory.split(',')
  const leftMode = historyMode[0]
  const rightMode = historyMode[1]
  // no param
  if (historyMode.length === 1) {
    if (leftMode === 'hash') {
      return createWebHashHistory('')
    } else if (leftMode === 'h5') {
      return createWebHistory('')
    }
  } // has param
  else if (historyMode.length === 2) {
    if (leftMode === 'hash') {
      return createWebHashHistory(rightMode)
    } else if (leftMode === 'h5') {
      return createWebHistory(rightMode)
    }
  }

  return createWebHistory('hash')
}

/** 获取当前页面按钮级别的权限 */
function getAuths(): Array<string> {
  return router.currentRoute.value.meta.auths as Array<string>
}

/**
 * 是否有按钮级别的权限（根据路由`meta`中的`auths`字段进行判断）
 *
 * @param value
 */
function hasAuth(value: string | Array<string>): boolean {
  if (!value) return false
  /** 从当前路由的`meta`字段里获取按钮级别的所有自定义`code`值 */
  const metaAuths = getAuths()
  if (!metaAuths) return false
  const isAuths = isString(value)
    ? metaAuths.includes(value)
    : isIncludeAllChildren(value, metaAuths) // 全部包含才返回true
  return isAuths ? true : false
}

function handleTopMenu(route) {
  if (route?.children && route.children.length > 1) {
    if (route.redirect) {
      return route.children.find(cur => cur.path === route.redirect)
    } else {
      return route.children[0]
    }
  } else {
    return route
  }
}

/**
 * 获取所有菜单中的第一个菜单（顶级菜单）
 *
 * @param tag
 */
function getTopMenu(tag = false): menuType {
  const permissionStore = usePermissionStore()
  const topMenu = handleTopMenu(permissionStore.wholeMenus[0]?.children[0])
  tag && useMultiTagsStore().handleTags('push', topMenu)
  return topMenu
}

export {
  addAsyncRoutes,
  addPathMatch,
  ascending,
  filterNoPermissionTree,
  filterTree,
  findRouteByPath,
  formatFlatteningRoutes,
  formatTwoStageRoutes,
  getAuths,
  getHistoryMode,
  getParentPaths,
  getTopMenu,
  handleAliveRoute,
  hasAuth,
  initRouter,
  isOneOfArray,
}
